﻿using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using CollegeSim.Domain.DTO;
using CollegeSim.Domain.Entities;
using CollegeSim.Domain.Interfaces.Repositories;
using CollegeSim.Domain.Interfaces.Services;

namespace CollegeSim.Domain.Services
{
    // Author: Ryan Redburn
    // Project: CollegeSim
    // Date: 2/8/2014

    #region [ Updates ]

    // Author: Ryan Redburn
    // Date: 5/6/2014
    // Revisions: Changed service to reflect version 2.0 changes.

    // Author: Ryan Redburn
    // Date: 5/12/2014
    // Revisions: Added Update() and Delete() methods. Fixed some spelling and added a few comments. Added exception handling to FindByID().

    // Author: Ryan Redburn
    // Date: 5/18/2014
    // Revisions: Removed Automapper mapping from InstructorDTO -> Instructor due to exception encapsulation problems. Wrote MapDTO() which now fulfills that functionality.
    //            Added missing exception handling to Commit(), Update(), and Delete().

    #endregion

    /// <summary>
    /// Service class used to interact with instructor objects.
    /// </summary>
    public class InstructorService : IInstructorService
    {
        #region [ Fields ]

        private readonly IInstructorRepository _instructorRepository;

        #endregion

        #region [ Constructors ]

        // Requires an instance of IInstructorRepository
        public InstructorService(IInstructorRepository instructorRepository)
        {
            _instructorRepository = instructorRepository;
        }

        #endregion

        #region [ Methods ]

        /// <summary>
        /// Returns a list of all instructors
        /// </summary>
        /// <returns>List of InstructorDto</returns>
        public List<InstructorDTO> GetAll()
        {
            // If invalid data is received from persistence an exception is thrown to indicate thusly
            try
            {
                var instructors = _instructorRepository.FindAll().Select(MapDTO).ToList(); // Validate data
                return Mapper.Map<List<InstructorDTO>>(instructors);
            }
            catch (ArgumentNullException)
            {
                throw new InvalidOperationException("Invalid data may have been received from persistence.");
            }
            catch (ArgumentOutOfRangeException)
            {
                throw new InvalidOperationException("Invalid data may have been received from persistence.");
            }
        }

        /// <summary>
        /// Returns an instructor with the given ID.
        /// </summary>
        /// <param name="instructorID">Integer instructor id</param>
        /// <returns>InstructorDto</returns>
        public InstructorDTO GetByID(int instructorID)
        {
            // Throws an exception for invalid instructor ID value
            if (instructorID < 0)
                throw new ArgumentOutOfRangeException("instructorID",
                    "instructorID does not accept an integer less than zero as an argument.");

            // If invalid data is received from persistence an exception is thrown to indicate thusly
            try
            {
                // Validate and return instructor
                var instructor = MapDTO(_instructorRepository.FindByID(instructorID));
                return Mapper.Map<InstructorDTO>(instructor);
            }
            catch (ArgumentNullException)
            {
                throw new InvalidOperationException("Invalid data may have been received from persistence.");
            }
            catch (ArgumentOutOfRangeException)
            {
                throw new InvalidOperationException("Invalid data may have been received from persistence.");
            }
        }

        /// <summary>
        /// Validates and commits an instructor for persistence.
        /// </summary>
        /// <param name="newInstructor">New instructor to save.</param>
        public void Commit(InstructorDTO newInstructor)
        {
            try
            {
                // Validate and submit instructor to persistence
                var instructor = MapDTO(newInstructor);
                _instructorRepository.Insert(Mapper.Map<InstructorDTO>(instructor));
            }
            catch (ArgumentNullException x1)
            {
                throw new ArgumentException(x1.Message);
            }
            catch (ArgumentOutOfRangeException x2)
            {
                throw new ArgumentException(x2.Message);
            }
        }

        /// <summary>
        /// Validates and commits an instructor for persistence.
        /// </summary>
        /// <param name="instructorID">ID of instructor to update.</param>
        /// <param name="instructorUpdate">Update information.</param>
        public void Update(int instructorID, InstructorDTO instructorUpdate)
        {
            // Throw exception if no instructor matching the given is found
            if (_instructorRepository.FindByID(instructorID) == null)
                throw new InvalidOperationException("No instructor matching the given exists.");
            try
            {
                // Validate instructorUpdate and submit to persistence
                var instructorArg = MapDTO(instructorUpdate);
                _instructorRepository.Update(Mapper.Map<InstructorDTO>(instructorArg));
            }
            catch (ArgumentNullException x1)
            {
                throw new ArgumentException(x1.Message);
            }
            catch (ArgumentOutOfRangeException x2)
            {
                throw new ArgumentException(x2.Message);
            }
        }

        /// <summary>
        /// Deletes an existing instructor.
        /// </summary>
        /// <param name="instructorDel">Instructor to delete.</param>
        public void Delete(InstructorDTO instructorDel)
        {
            // Throw exception if no instructor matching the given exists
            if (_instructorRepository.FindByID(instructorDel.InstructorID) == null)
                throw new InvalidOperationException("No instructor matching the given exists.");

            try
            {
                // Submit instructor to persistence for deletion
                var instructor = MapDTO(instructorDel);
                _instructorRepository.Delete(Mapper.Map<InstructorDTO>(instructor));
            }
            catch (ArgumentNullException x1)
            {
                throw new ArgumentException(x1.Message);
            }
            catch (ArgumentOutOfRangeException x2)
            {
                throw new ArgumentException(x2.Message);
            }
        }

        #endregion

        #region [ Internal Methods ]

        /// <summary>
        /// Maps an InstructorDTO to a domain Instructor.
        /// </summary>
        /// <param name="dto">The InstructorDTO to map.</param>
        /// <returns>Mapped domain Instructor.</returns>
        private Instructor MapDTO(InstructorDTO dto)
        {
            var mappedInstructor = new Instructor
            {
                InstructorID = dto.InstructorID,
                FirstName = dto.FirstName,
                LastName = dto.LastName,
                PhoneNumber = dto.PhoneNumber,
                Email = dto.Email,
                OfficeNumber = dto.OfficeNumber
            };

            return mappedInstructor;
        }

        #endregion
    }
}
